package com.seleniti.enpareja.web.desktop.client.ui.view;

import static com.google.gwt.dom.client.BrowserEvents.CLICK;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.ioc.commons.impl.gwt.client.ui.Css3Util;
import org.ioc.commons.impl.gwt.client.ui.Css3Util.Css3EffectQueue.AppearEffectType;
import org.ioc.commons.impl.gwt.client.ui.Css3Util.Css3EffectQueue.DisappearEffectType;
import org.ioc.commons.impl.gwt.client.ui.Css3Util.TransitionEndListener;
import org.ioc.commons.impl.gwt.client.ui.Css3Util.VisibilityModeStyle;
import org.ioc.commons.impl.gwt.client.ui.HasEnabledWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasTextWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasValueWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasVisibilityWrapper;
import org.ioc.commons.ui.HasEnabled;
import org.ioc.commons.ui.HasText;
import org.ioc.commons.ui.HasValue;
import org.ioc.commons.ui.HasVisibility;
import org.ioc.commons.ui.impl.HasEnabledAdapter;
import org.ioc.commons.ui.impl.HasTextAdapter;
import org.ioc.commons.ui.impl.HasValueAdapter;
import org.ioc.commons.ui.impl.HasVisibilityAdapter;
import org.ioc.commons.ui.impl.OnChangeCommand;
import org.ioc.commons.utils.Format;

import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CustomPushButton;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DialogBox.CaptionImpl;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Html5DateBox;
import com.google.gwt.user.client.ui.Html5TimeBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.NoSelectionModel;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.model.integration.database.domain.HomeTask;
import com.seleniti.enpareja.controller.ui.view.HomeTasksView;
import com.seleniti.enpareja.controller.ui.view.HomeTasksView.Action;
import com.seleniti.enpareja.controller.ui.view.HomeTasksView.Event;
import com.seleniti.enpareja.controller.ui.view.HomeTasksView.Operation;
import com.seleniti.enpareja.controller.ui.view.HomeTasksView.Presenter;
import com.seleniti.enpareja.controller.ui.view.HomeTasksViewPresenter;
import com.seleniti.enpareja.web.desktop.client.ui.cell.EnParejaTemplate;
import com.seleniti.enpareja.web.desktop.client.ui.resources.CommonResources;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker.SelectionMode;
import com.seleniti.enpareja.web.desktop.client.ui.widget.PendingMark;

public class HomeTasksViewWidget extends EnParejaBaseView<Event, Operation, Action, Presenter> implements HomeTasksView {

	private static TemplateUiBinder uiBinder = GWT.create(TemplateUiBinder.class);

	interface TemplateUiBinder extends UiBinder<Widget, HomeTasksViewWidget> {
	}

	private static EnParejaTemplate template = GWT.create(EnParejaTemplate.class);

	public static interface Style extends CssResource {
		String container();

		String columnsHeader();

		String done();

		String pending();

		String down();
	}

	@UiField(provided = true)
	CellList<HomeTask> cellList;

	@UiField
	Style style;

	@UiField
	CustomPushButton btnAdd;

	@UiField
	ToggleButton btnModify;

	@UiField
	ToggleButton btnDelete;

	// @UiField
	// HTML lblToolInfo;

	@UiField
	CommonResources common;

	@UiField(provided = true)
	Checker<HomeTask> checker;

	@UiField
	Button btnMadeByMember1;

	@UiField
	Button btnMadeByMember2;

	@UiField
	Button btnMadeByBoth;

	@UiField
	Button btnScheduleForMember1;

	@UiField
	Button btnScheduleForMember2;

	@UiField
	Button btnScheduleForBoth;

	@UiField
	Button btnOk;

	@UiField
	VerticalPanel doneSettingsPanel;

	@UiField
	Html5DateBox lastTime;

	@UiField
	ToggleButton btnDoneOrPending;

	@UiField
	PendingMark pendingMark;

	@UiField
	Panel markContainer;

	@UiField
	DeckLayoutPanel mainPanel;

	@UiField(provided = true)
	CellList<String> basicTasksList;

	@UiField(provided = true)
	Checker<String> basicTasksChecker;

	@UiField
	Html5DateBox scheduleDate;

	@UiField
	Html5TimeBox scheduleTime;

	@UiField
	Panel schedulePanel;

	@UiField
	FocusPanel btnSchedule;

	@UiField
	Label lblSchedule;

	@UiField
	Panel scheduleBar;

	private ListDataProvider<HomeTask> listDataProvider;

	private boolean loading;

	private ProvidesKey<HomeTask> keyProvider;

	private ActionMode actionMode;

	private boolean enabled = true;

	private DialogBox settingsDialog;

	private HomeTask settingsForThis;

	private CaptionImpl caption;

	private ListDataProvider<String> basicTasksProvider;

	public HomeTasksViewWidget() {
		this.presenter = new HomeTasksViewPresenter(this, eventbus, operationManager, actionController);

		this.keyProvider = new ProvidesKey<HomeTask>() {

			@Override
			public Object getKey(HomeTask item) {
				return item != null ? item.getHomeTaskId() : null;
			}
		};
		SafeHtmlRenderer<HomeTask> renderer = new AbstractSafeHtmlRenderer<HomeTask>() {

			@Override
			public SafeHtml render(HomeTask object) {
				if (object != null) {
					SafeHtml listElement = null;
					if (object.getLastTime() != null) {
						String extText = null;
						if (object.getMadeBy() != null) {
							String madeBy = "--";
							switch (object.getMadeBy()) {
							case BOTH:
								madeBy = "ambos";
								break;
							case MEMBER_1:
								madeBy = member1().getText() != null ? member1().getText() : "tí";
								break;
							case MEMBER_2:
								madeBy = member2().getText() != null ? member2().getText() : "tu pareja";
								break;
							}
							extText = Format.substitute("Hecho por {0} el {1}", madeBy,
									DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT).format(object.getLastTime()));
						} else {
							extText = Format.substitute("Hecho el {0}",
									DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT).format(object.getLastTime()));
						}
						SafeHtml ext = template.divImg(common.tick32x32().getSafeUri(), style.done(),
								SafeHtmlUtils.fromString(extText));
						listElement = template.listElementExt(common.style().tri_right() + " " + style.done(),
								object.getText(), ext);

					} else {
						SafeHtml ext = template.div(style.pending() + " " + common.style().yellowButton(),
								SafeHtmlUtils.fromString("Pendiente"));
						listElement = template.listElementExt(common.style().tri_right() + " " + style.pending(),
								object.getText(), ext);
					}
					return listElement;
				} else {
					return SafeHtmlUtils.fromString("-- no element --");
				}
			}
		};
		this.cellList = new CellList<HomeTask>(new AbstractSafeHtmlCell<HomeTask>(renderer, "click") {

			@Override
			protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
				if (data != null) {
					sb.append(data);
				}
			}

			public void onBrowserEvent(Context context, Element parent, HomeTask value, NativeEvent event,
					ValueUpdater<HomeTask> valueUpdater) {
				super.onBrowserEvent(context, parent, value, event, valueUpdater);
				if (CLICK.equals(event.getType())) {
					EventTarget eventTarget = event.getEventTarget();
					if (!Element.is(eventTarget)) {
						return;
					}
					if (parent.getFirstChildElement().isOrHasChild(Element.as(eventTarget))) {
						// Ignore clicks that occur outside of the main element.
						if (valueUpdater != null) {
							valueUpdater.update(value);
						}
					}
				}
			}

		}, keyProvider);

		this.cellList.setValueUpdater(new ValueUpdater<HomeTask>() {

			@Override
			public void update(HomeTask value) {
				if (actionMode != null) {
					switch (actionMode) {
					case MODIFY_ELEMENT:
						modifyElement(value);
						break;
					case SWITCH_LIST:
						switchElementFromList(value);
						break;
					case DELETE_ELEMENT:
					default:
						/*
						 * Nothing to do
						 */
					}
				}
			}
		});

		HTML empty = new HTML("Ningún elemento en tu lista");
		this.cellList.setEmptyListWidget(empty);

		this.cellList.setSelectionModel(new NoSelectionModel<HomeTask>(keyProvider));

		this.listDataProvider = new ListDataProvider<HomeTask>();
		this.listDataProvider.addDataDisplay(cellList);

		this.checker = new Checker<HomeTask>(50);
		this.checker.addDataDisplay(cellList);

		this.basicTasksList = new CellList<String>(new TextCell());
		this.basicTasksList.setPageSize(Integer.MAX_VALUE);
		this.basicTasksProvider = new ListDataProvider<String>();
		this.basicTasksProvider.addDataDisplay(basicTasksList);

		this.basicTasksChecker = new Checker<String>(50);
		this.basicTasksChecker.addDataDisplay(basicTasksList);

		initWidget(uiBinder.createAndBindUi(this));

		empty.setStyleName(common.style().emptyList());

		this.caption = new CaptionImpl();
		caption.setHTML(template.caption(common.style().caption(), SafeHtmlUtils.fromString("Hecho por:")));
		this.settingsDialog = new DialogBox(false, false, caption);
		this.settingsDialog.setWidget(doneSettingsPanel);
		this.settingsDialog.setSize("100%", "100%");
		this.settingsDialog.addStyleName(common.style().dialog());
	}

	@Override
	public List<HomeTask> homeTasksList() {
		return this.listDataProvider.getList();
	}

	@UiHandler("btnCancel")
	void onCancelButtonClicked(ClickEvent evt) {
		this.hideTaskDoneSettings();
	}

	@UiHandler("btnModify")
	void onChangeButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.presenter.onModifyButtonToggled(enabled);

		if (enabled) {
			btnDelete.setDown(false);
		}
	}

	@UiHandler("btnDelete")
	void onDeleteButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(enabled ? ActionMode.DELETE_ELEMENT : ActionMode.SWITCH_LIST);

		if (enabled) {
			btnModify.setDown(false);
		}
	}

	@UiHandler("btnAdd")
	void onAddElementButtonClicked(ClickEvent evt) {
		this.addNewElement();
	}

	@UiHandler("checker")
	void onCheckerSelectionChanged(SelectionChangeEvent evt) {
		presenter.onSelectionChanged();
	}

	@Override
	public void addNewElement() {
		this.presenter.onAddNewElement();
	}

	@Override
	public void setLoading(boolean loading) {
		this.loading = loading;
		btnAdd.setEnabled(!loading);
		btnModify.setEnabled(!loading);
		btnDelete.setEnabled(!loading);
	}

	@Override
	public boolean isLoading() {
		return this.loading;
	}

	@Override
	public void setActionMode(ActionMode mode) {
		this.actionMode = mode;
		switch (mode) {
		case DELETE_ELEMENT:
			checker.setSelectionMode(SelectionMode.Multiselection);
			checker.setVisible(true);
			btnDelete.setDown(true);
			btnModify.setDown(false);
			break;
		case SWITCH_LIST:
			checker.setVisible(false);
			btnDelete.setDown(false);
			btnModify.setDown(false);
			break;
		case MODIFY_ELEMENT:
			checker.setSelectionMode(SelectionMode.SingleSelection);
			checker.setVisible(true);
			btnDelete.setDown(false);
			btnModify.setDown(true);
			break;
		}

		this.presenter.onActionModeChanged();
	}

	@Override
	public ActionMode getActionMode() {
		return actionMode;
	}

	@Override
	public void switchElementFromList(HomeTask value) {
		presenter.onSwitchElementFromList(value);
	}

	@Override
	public Set<HomeTask> getSelection() {
		Set<HomeTask> selection = new HashSet<HomeTask>();
		boolean selected[] = this.checker.getSelection();
		for (int i = 0; i < selected.length; i++) {
			boolean elmSelected = selected[i];

			if (elmSelected) {
				selection.add(this.homeTasksList().get(i));
			}
		}

		return selection;
	}

	@Override
	public boolean isEnabled() {
		return this.enabled;
	}

	@Override
	public void setEnabled(boolean enabled) {
		this.enabled = true;
		HasEnabledWrapper.setEnabled(enabled, btnAdd, btnDelete, btnModify);
		this.setTitle(enabled ? "" : "¡Asociate a tu pareja antes de empezar a usar todas las opciones!");
	}

	@Override
	public HasText member1() {
		return HasTextAdapter.forField("member1", this).onChange(new OnChangeCommand<String>() {

			@Override
			public void execute(String value) {
				btnMadeByMember1.setText(value);
				btnScheduleForMember1.setText(value);
			}
		});
		// return HasTextWrapper.from(btnMadeByMember1, this);
	}

	@Override
	public HasText member2() {
		return HasTextAdapter.forField("member2", this).onChange(new OnChangeCommand<String>() {

			@Override
			public void execute(String value) {
				btnMadeByMember2.setText(value);
				btnScheduleForMember2.setText(value);
			}
		});
		// return HasTextWrapper.from(btnMadeByMember2, this);
	}

	@UiHandler("btnMadeByMember1")
	void onMadeByMember1ButtonClicked(ClickEvent evt) {
		this.markSelectedAsMadeByMember1();
	}

	@UiHandler("btnOk")
	void onOkButtonClicked(ClickEvent evt) {
		this.markSelectedAsMadeByMember1();
	}

	@UiHandler("btnMadeByMember2")
	void onMadeByMember2ButtonClicked(ClickEvent evt) {
		this.markSelectedAsMadeByMember2();
	}

	@UiHandler("btnMadeByBoth")
	void onMadeByMemberBothButtonClicked(ClickEvent evt) {
		this.markSelectedAsMadeByBoth();
	}

	@UiHandler("btnAddBasics")
	void onAddBasicsButtonClicked(ClickEvent evt) {
		presenter.onAddSelectedBasicElements();
	}

	@UiHandler("btnCancelBasics")
	void onCancelBasicsButtonClicked(ClickEvent evt) {
		presenter.onCancelAddingSelectedBasicElements();
	}

	@Override
	public HasValue<Date> lastTime() {
		return HasValueWrapper.from(lastTime, true, this);
	}

	@Override
	public void markSelectedAsMadeByMember1() {
		this.presenter.onMarkSelectedAsMadeByMember1();
	}

	@Override
	public void markSelectedAsMadeByMember2() {
		this.presenter.onMarkSelectedAsMadeByMember2();
	}

	@Override
	public void markSelectedAsMadeByBoth() {
		this.presenter.onMarkSelectedAsMadeByBoth();
	}

	@Override
	public void showTaskDoneSettings(HomeTask settingsForThis, boolean changeDate) {
		this.settingsForThis = settingsForThis;
		// lastTime.setVisible(changeDate);
		boardShown().setValue(Board.FORM);
	}

	@Override
	public HomeTask getSettingsForThis() {
		return this.settingsForThis;
	}

	@Override
	public void hideTaskDoneSettings() {
		boardShown().setValue(Board.TASKS_LIST);
	}

	@Override
	public void modifyElement(HomeTask element) {
		this.presenter.onModifyElement(element);
	}

	@UiHandler("btnDoneOrPending")
	void onDoneOrPendingButtonToggled(ValueChangeEvent<Boolean> evt) {
		this.presenter.onDoneOrPendingButtonToggled();
		markContainer.setStyleName(style.down(), evt.getValue());
	}

	@Override
	public HasValue<Boolean> onlyPendingFilter() {
		return HasValueWrapper.from(btnDoneOrPending, true, this);
	}

	@Override
	public HasValue<Integer> pendingCounter() {
		return this.pendingMark;
	}

	@Override
	public void scrollToElement(final int idx) {
		Scheduler.get().scheduleDeferred(new ScheduledCommand() {

			@Override
			public void execute() {
				Element rowElement = cellList.getRowElement(idx);
				if (rowElement != null) {
					rowElement.scrollIntoView();
				}
			}
		});
	}

	@Override
	public HasValue<Board> boardShown() {
		return HasValueAdapter.<HomeTasksView.Board> forField("boardShown", this).onChange(
				new OnChangeCommand<HomeTasksView.Board>() {

					@Override
					public void execute(Board value) {
						if (value == null) {
							value = Board.TASKS_LIST;
						}

						switch (value) {
						case TASKS_LIST:
							mainPanel.showWidget(0);
							settingsDialog.hide();
							break;
						case FORM:
							mainPanel.showWidget(0);
							settingsDialog.center();
							break;
						case BASIC_TASKS_LIST:
							mainPanel.showWidget(1);
							settingsDialog.hide();
							break;
						case SCHEDULE_FORM:
							mainPanel.showWidget(2);
							settingsDialog.hide();
							break;
						default:
							throw new IllegalArgumentException("Unknown board: " + value);
						}

					}
				});
	}

	@Override
	public boolean goBoardBack() {
		if (boardShown().getValue() == Board.FORM) {
			hideTaskDoneSettings();
			return true;
		}

		return false;
	}

	@Override
	public HasValue<CoupleType> coupleType() {
		return HasValueAdapter.forField("coupleType", this, CoupleType.USERS_COUPLE).onChange(
				new OnChangeCommand<CoupleType>() {

					@Override
					public void execute(CoupleType value) {
						if (value == null) {
							value = CoupleType.USERS_COUPLE;
						}
						switch (value) {
						case USERS_COUPLE:
							caption.setHTML(template.caption(common.style().caption(),
									SafeHtmlUtils.fromString("Hecho por:")));
							HasVisibilityWrapper.setVisible(true, btnMadeByMember1, btnMadeByMember2, btnMadeByBoth);
							btnOk.setVisible(false);
							break;
						case SINGLE_USER:
						case TESTING_MODE_COUPLE:
							caption.setHTML(template.caption(common.style().caption(),
									SafeHtmlUtils.fromString("Hecho:")));
							HasVisibilityWrapper.setVisible(false, btnMadeByMember1, btnMadeByMember2, btnMadeByBoth);
							btnOk.setVisible(true);
							break;
						default:
							throw new IllegalArgumentException("Unknow type: " + value);
						}

					}
				});
	}

	@Override
	public List<String> basicTasks() {
		return this.basicTasksProvider.getList();
	}

	@Override
	public Set<String> selectedBasicTasks() {

		return this.basicTasksChecker.getSelectedItems();
	}

	@Override
	public void showBasics() {
		this.presenter.onShowBasics();
	}

	@Override
	public HasValue<LoggedUserInfo> loggedUserInfo() {
		return HasValueAdapter.<LoggedUserInfo> forField("loggedUserInfo", this);
	}

	@UiHandler("btnScheduleForMember1")
	void onScheduleForMember1ButtonClicked(ClickEvent evt) {
		this.presenter.onScheduleSuggestedForMember1();
	}

	@UiHandler("btnScheduleForMember2")
	void onScheduleForMember2ButtonClicked(ClickEvent evt) {
		this.presenter.onScheduleSuggestedForMember2();
	}

	@UiHandler("btnScheduleForBoth")
	void onScheduleForMemberBothButtonClicked(ClickEvent evt) {
		this.presenter.onScheduleSuggestedForBoth();
	}

	@Override
	public HasText scheduleSuggestionText() {
		return HasTextWrapper.from(lblSchedule, this);
	}

	@Override
	public HasVisibility scheduleSuggestionVisibility() {
		return HasVisibilityAdapter.forField("scheduleSuggestionVisibility", this, schedulePanel.isVisible()).onChange(
				new OnChangeCommand<Boolean>() {

					@Override
					public void execute(Boolean value) {
						if (Boolean.TRUE.equals(value)) {
							scheduleBar.setVisible(true);
							Css3Util.getToolkit(schedulePanel).appear(AppearEffectType.MOVE_FROM_BOTTOM);
						} else {
							Css3Util.getToolkit(schedulePanel)
									.disappear(DisappearEffectType.MOVE_TO_BOTTOM,
											VisibilityModeStyle.USE_DISPLAY_PROPERTY).getCss3Style()
									.addTransitionEndListener(new TransitionEndListener() {

										@Override
										public void onTransitionEnd(NativeEvent e) {
											scheduleBar.setVisible(false);
											this.removeTransitionEndListener(Element.as(e.getEventTarget()));
										}
									});
						}

					}
				});
		// return HasVisibilityWrapper.from(schedulePanel, this);
	}

	@Override
	public HasEnabled scheduleSuggestionEnabled() {
		return HasEnabledAdapter.forField("scheduleSuggestionEnabled", this, scheduleBar.isVisible());
	}

	@Override
	public void hideScheduleForm() {
		presenter.onHideScheduleForm();
	}

	@Override
	public HasValue<Date> scheduleDate() {
		return this.scheduleDate;
	}

	@Override
	public HasValue<Date> scheduleTime() {
		return this.scheduleTime;
	}

	@UiHandler("btnCloseSchedulePanel")
	void onCloseSchedulePanelButtonClicked(ClickEvent evt) {
		presenter.onHideScheduleSuggestion();
	}

	@UiHandler("btnSchedule")
	void onSchedulePanelClicked(ClickEvent evt) {
		if (this.scheduleSuggestionEnabled().isEnabled()) {
			presenter.onScheduleSuggestion();
		}
	}

	@UiHandler("btnScheduleCancel")
	void onScheduleCancelButtonClicked(ClickEvent evt) {
		presenter.onHideScheduleForm();
	}

}
